package online.model.session;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import online.listener.SessionMutexListener;

/**
 * セション属性制御
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class SessionAttribute implements Serializable {
	/** serialVersionUID */
	private static final long serialVersionUID = 3685688787504825480L;

	/** セション保存キー */
	private static final String PRE_SESSION_KEY = SessionAttribute.class.getName();

	/** セション保存オブジェクト取得用マップ */
	private final Map<String, Serializable> smap;
	/** セション保存オブジェクト再設定用マップ */
	private final Map<String, Serializable> mod = new HashMap<>();

	/**
	 * コンストラクタ
	 *
	 * @param request リクエスト
	 */
	public SessionAttribute(final HttpServletRequest request) {
		this.smap = getAttributeMap(request);
	}

	/**
	 * セション保存用マップ取得
	 *
	 * @param request リクエスト
	 */
	public void saveSessionParameters(final HttpServletRequest request) {
		setAttributeMap(this.mod, request);
	}

	/**
	 * セション保持オブジェクト取得
	 *
	 * @param key キー
	 * @return セション保持オブジェクト
	 */
	public Object getSessionParameter(final String key) {
		String name = toSessionAttributeKey(key);
		if (!this.mod.containsKey(name)) {
			if (this.smap.get(name) != null) {
				this.mod.put(name, this.smap.get(name));
			}
		}
		return this.mod.get(name);
	}

	/**
	 * セション保持オブジェクト設定
	 *
	 * @param key キー
	 * @param obj 記憶オブジェクト
	 */
	public void setSessionParameter(final String key, final Serializable obj) {
		this.mod.put(toSessionAttributeKey(key), obj);
	}

	/**
	 * セション保持キー取得
	 *
	 * @param key 元キー文字列
	 * @return セション保持キー
	 */
	public static String toSessionAttributeKey(final String key) {
		return PRE_SESSION_KEY + key;
	}

	/**
	 * セション情報設定
	 *
	 * @param map マップ
	 * @param request リクエストオブジェクト
	 */
	public static void setAttributeMap(
			final Map<String, ? extends Serializable> map, final HttpServletRequest request) {
		// セションオブジェクト取得
		if (map != null && request != null) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				synchronized (SessionMutexListener.getMutex(session)) {
					for (final Entry<String, ? extends Serializable> me : map.entrySet()) {
						String key = me.getKey();
						if (key.startsWith(PRE_SESSION_KEY)) {
							session.setAttribute(key, me.getValue());
						}
					}
				}
			}
		}
	}

	/**
	 * セション情報保存マップ取得
	 *
	 * @param request リクエストオブジェクト
	 * @return セション記憶オブジェクト
	 */
	public static Map<String, Serializable> getAttributeMap(final HttpServletRequest request) {
		// セションオブジェクト取得
		if (request != null) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Map<String, Serializable> map = new HashMap<>();
				synchronized (SessionMutexListener.getMutex(session)) {
					for (Enumeration<String> en = session.getAttributeNames();
							en.hasMoreElements();) {
						String key = en.nextElement();
						if (key.startsWith(PRE_SESSION_KEY)) {
							map.put(key, Serializable.class.cast(session.getAttribute(key)));
						}
					}
				}
				return map;
			}
		}
		return null;
	}

	/**
	 * セション共通モデル情報設定
	 *
	 * @param request リクエストオブジェクト
	 * @param um 設定元モデル
	 */
	public static void setSessionUser(final HttpServletRequest request, final SessionUser um) {
		// セションオブジェクト取得
		HttpSession session = request.getSession(false);
		if (session != null) {
			session.setAttribute(session.getId(), um);
		}
	}

	/**
	 * セション共通モデル情報取得
	 *
	 * @param request リクエストオブジェクト
	 * @return セション共通情報
	 */
	public static SessionUser getSessionUser(final HttpServletRequest request) {
		// セションオブジェクト取得
		HttpSession session = request.getSession(false);
		if (session != null) {
			// 共通セション情報設定
			return SessionUser.class.cast(session.getAttribute(session.getId()));
		}
		return null;
	}
}
